package com.taomus.mytools.simplec;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Stack;

import com.taomus.mytools.simplec.common.SyntaxTuple;
import static com.taomus.mytools.simplec.common.SyntaxTuple.*;
import com.taomus.mytools.simplec.common.Token;
import com.taomus.mytools.simplec.common.Word;
import com.taomus.mytools.simplec.common.WordEnum;
import com.taomus.mytools.simplec.utils.ErrorEnum;
import com.taomus.mytools.simplec.utils.ErrorUtils;

public class Scope {
	private LinkedList<SyntaxTuple> stlist = null;
	private Map<String, Token> symbol = new HashMap<>();
	private Stack<Token> symbolScope = new Stack<>();
	private int level = 0;

	public Scope(final LinkedList<SyntaxTuple> inStlist) {
		this.stlist = inStlist;
	}

	public void scan() {
		for (SyntaxTuple st : stlist) {
			if (st.getOpCode() == WordEnum.MOV) {
				if(st.getCode(T3) == WordEnum.FUNCL){
					st.getToken(T1).setLevel(this.level);
					this.level += 1;
					st.opcode(Word.TK_FUNC);
					symbolScope.push(st.getToken(T1));
					continue;
				}
				varDefined(st);
			}else if(st.getOpCode() == WordEnum.FUNCR){
				this.level -= 1;
				st.t1(symbolScope.pop());
			}else if(this.isOperation(st.getOpCode())){
				Token t1 = st.getToken(T1);
				Token t2 = st.getToken(T2);
				Token t3 = st.getToken(T3);
				if(this.contains(t1)){
					st.t1(this.getToken(t1));
					if(this.contains(t2)){
						st.t2(this.getToken(t2));
					}else if(this.isExclude(t2.getCode())){
						ErrorUtils.error(ErrorEnum.VAR_UNDEFINED, t2);
					}
				}else if(this.isExclude(t1.getCode())){
					ErrorUtils.error(ErrorEnum.VAR_UNDEFINED, t1);
				}
			}
		}
	}
	
	private boolean isExclude(WordEnum code){
		switch(code){
		case TEMP:
		case NUMBER:
			return false;
		default:
			return true;
		}
	}
	
	private boolean isOperation(WordEnum opcode){
		switch(opcode){
		case ADD:
		case MUL:
		case SUB:
		case DIV:
		case GT:
		case LT:
		case GE:
		case LE:
		case NE:
		case EQ:
			return true;
		default:
			return false;
		}
	}
	
 	private boolean contains(Token key){
		boolean isExist = this.symbol.containsKey(key.getSymbol() + level);
		if(!isExist){
			isExist = this.symbol.containsKey(key.getSymbol());
		}
		return isExist;
	}
	private Token getToken(Token key){
		Token tk = this.symbol.get(key.getSymbol() + level);
		if(null == tk){
			tk = this.symbol.get(key.getSymbol());
		}
		key.setAlias(tk.getAlias());
		key.setAttribute(tk.getAttribute());
		key.setDef(tk.isDef());
		key.setLevel(tk.getLevel());
		return key;
	}
	
	/**
	 * 变量定义
	 * @param st
	 */
	private void varDefined(SyntaxTuple st) {
		Token t1 = st.getToken(T1);
		if (!t1.isDef()) {
			t1.setLevel(level);
			t1.setAlias(t1.getSymbol() + level);
			symbol.put(t1.getAlias(), t1);
			symbol.put(t1.getSymbol(), t1);
			Token t3 = st.getToken(T3);
			switch (t3.getCode()) {
			case NUMBER:
				st.t2(Word.KW_INT);
				t1.setAttribute(WordEnum._INT);
				t1.setDef(true);
				break;
			case CHAR:
				st.t2(Word.KW_CHAR);
				t1.setAttribute(WordEnum._CHAR);
				t1.setDef(true);
				break;
			case DECIMAL:
				st.t2(Word.KW_DOUBLE);
				t1.setAttribute(WordEnum._DOUBLE);
				t1.setDef(true);
				break;
			case STRING:
				st.t2(Word.KW_STRING);
				t1.setAttribute(WordEnum._STRING);
				t1.setDef(true);
				break;
			case ID:
				if(this.contains(t3)){
					t3 = this.getToken(t3);
					st.t3(t3);
					if (!t3.isDef()) {
						ErrorUtils.error(ErrorEnum.VAR_UNDEFINED,t3);
					}else{
						t1.setAttribute(t3.getAttribute());
						t1.setDef(true);
						st.t2(new Token(t3.getAttribute()));
					}
				}
				break;
			default:
				break;
			}
		}
	}
}
